package kz.pompei.ifacer.main;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;

import kz.pompei.ifacer.clipboard.WidgetSelection;
import kz.pompei.ifacer.clipboard.WidgetTransfer;
import kz.pompei.ifacer.grot.GrotPanel;
import kz.pompei.ifacer.grot.acts.Act;
import kz.pompei.ifacer.grot.acts.ActGroup;
import kz.pompei.ifacer.grot.acts.AddAct;
import kz.pompei.ifacer.grot.acts.ChangeFieldsAct;
import kz.pompei.ifacer.grot.acts.ChangeIndexAct;
import kz.pompei.ifacer.model.widgets.Widget;
import kz.pompei.ifacer.model.widgets.WidgetButton;
import kz.pompei.ifacer.model.widgets.WidgetCheck;
import kz.pompei.ifacer.model.widgets.WidgetForm;
import kz.pompei.ifacer.model.widgets.WidgetGrid;
import kz.pompei.ifacer.model.widgets.WidgetLabel;
import kz.pompei.ifacer.model.widgets.WidgetRect;

public class ActionManager {
  private final GrotTabPanel worker;
  private final Set<AbstractAction1> actions = new HashSet<>();
  public final List<AbstractAction1> additionActions = new ArrayList<>();
  
  public ActionManager(GrotTabPanel worker) {
    this.worker = worker;
    
    addAdditionAction("Форма", new WidgetForm());
    addAdditionAction("Кнопка", new WidgetButton());
    addAdditionAction("Метка", new WidgetLabel());
    addAdditionAction("Галочка", new WidgetCheck());
    addAdditionAction("Таблица", new WidgetGrid());
  }
  
  public final AbstractAction1 save = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      save(false, grot);
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.actContainer.modified());
    }
  });
  
  public final AbstractAction1 saveAll = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      for (GrotPanel grot : worker.getGrotList()) {
        save(false, grot);
      }
    }
    
    @Override
    protected void update() {
      boolean modified = false;
      for (GrotPanel grot : worker.getGrotList()) {
        modified = modified || grot.actContainer.modified();
      }
      setEnabled(modified);
    }
  });
  
  public final AbstractAction1 saveAs = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      save(true, grot);
    }
    
    @Override
    protected void update() {
      setEnabled(worker.getActiveGrot() != null);
    }
  });
  
  public final AbstractAction1 open = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = open();
      if (grot == null) return;
      grot.actContainer.fix();
      worker.addNewAndSelect(grot);
    }
  });
  
  public final AbstractAction1 undo = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      grot.undo();
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.actContainer.canUndo());
    }
  });
  public final AbstractAction1 redo = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      grot.redo();
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.actContainer.canRedo());
    }
  });
  
  public final AbstractAction1 exportToPng = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      exportImage(grot);
    }
    
    @Override
    protected void update() {
      setEnabled(worker.getActiveGrot() != null);
    }
  });
  
  public final AbstractAction1 elementLower = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      if (grot.selectedUuids.size() != 1) return;
      UUID uuid = grot.selectedUuids.iterator().next();
      int backIndex = grot.actContainer.storage.getIndexByUuid(uuid);
      if (backIndex <= 0) return;
      int forwardIndex = backIndex - 1;
      grot.actContainer.addAct(new ChangeIndexAct(uuid, forwardIndex, backIndex));
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      if (grot.selSize() != 1) {
        setEnabled(false);
        return;
      }
      UUID uuid = grot.selectedUuids.iterator().next();
      int index = grot.actContainer.storage.getIndexByUuid(uuid);
      setEnabled(index > 0);
    }
  });
  
  public final AbstractAction1 elementUpper = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      if (grot.selectedUuids.size() != 1) return;
      UUID uuid = grot.selectedUuids.iterator().next();
      int backIndex = grot.actContainer.storage.getIndexByUuid(uuid);
      if (backIndex >= grot.actContainer.storage.size() - 1) return;
      int forwardIndex = backIndex + 1;
      grot.actContainer.addAct(new ChangeIndexAct(uuid, forwardIndex, backIndex));
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      if (grot.selSize() != 1) {
        setEnabled(false);
        return;
      }
      UUID uuid = grot.selectedUuids.iterator().next();
      int index = grot.actContainer.storage.getIndexByUuid(uuid);
      setEnabled(index < grot.actContainer.storage.size() - 1);
    }
  });
  
  public final AbstractAction1 elementTop = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      if (grot.selectedUuids.size() != 1) return;
      UUID uuid = grot.selectedUuids.iterator().next();
      int backIndex = grot.actContainer.storage.getIndexByUuid(uuid);
      if (backIndex >= grot.actContainer.storage.size() - 1) return;
      int forwardIndex = grot.actContainer.storage.size() - 1;
      grot.actContainer.addAct(new ChangeIndexAct(uuid, forwardIndex, backIndex));
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      if (grot.selSize() != 1) {
        setEnabled(false);
        return;
      }
      UUID uuid = grot.selectedUuids.iterator().next();
      int index = grot.actContainer.storage.getIndexByUuid(uuid);
      setEnabled(index < grot.actContainer.storage.size() - 1);
    }
  });
  
  public final AbstractAction1 delete = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      if (grot.selectedUuids.size() == 0) return;
      grot.deleteSelected();
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 0);
    }
  });
  
  public final AbstractAction1 elementBottom = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      if (grot.selectedUuids.size() != 1) return;
      UUID uuid = grot.selectedUuids.iterator().next();
      int backIndex = grot.actContainer.storage.getIndexByUuid(uuid);
      if (backIndex <= 0) return;
      int forwardIndex = 0;
      grot.actContainer.addAct(new ChangeIndexAct(uuid, forwardIndex, backIndex));
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      if (grot.selSize() != 1) {
        setEnabled(false);
        return;
      }
      UUID uuid = grot.selectedUuids.iterator().next();
      int index = grot.actContainer.storage.getIndexByUuid(uuid);
      setEnabled(index > 0);
    }
  });
  
  private void exportImage(GrotPanel grot) throws Exception {
    JFileChooser fc = new JFileChooser();
    fc.setFileFilter(new FileFilter() {
      @Override
      public String getDescription() {
        return "png files";
      }
      
      @Override
      public boolean accept(File f) {
        if (f.isDirectory()) return true;
        return f.getAbsolutePath().endsWith(".png");
      }
    });
    if (JFileChooser.APPROVE_OPTION != fc.showSaveDialog(null)) return;
    File file = fc.getSelectedFile();
    
    if (!file.getAbsolutePath().endsWith(".png")) {
      file = new File(file.getAbsolutePath() + ".png");
    }
    
    ImageIO.write(grot.getExportImage(), "png", file);
  }
  
  private final AbstractAction1 add(AbstractAction1 action) {
    actions.add(action);
    return action;
  }
  
  private void save(boolean saveAs, GrotPanel grot) throws Exception {
    if (grot.workingFile == null || saveAs) {
      if (!grot.changeWorkingFile()) return;
      if (grot.workingFile == null) return;
      if (grot.workingFile.exists()) {
        if (JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog(grot,
            "Вы действительно хотите заменить файл " + grot.workingFile + " на новый?",
            "Опасность потери данных", JOptionPane.YES_NO_OPTION)) return;
      }
    }
    
    if (grot.workingFile == null) return;
    
    {
      FileOutputStream out = new FileOutputStream(grot.workingFile);
      grot.save(out);
      out.close();
      grot.actContainer.fix();
      worker.updates.fire();
    }
  }
  
  private GrotPanel open() throws Exception {
    JFileChooser fc = new JFileChooser();
    fc.setFileFilter(new FileFilter() {
      @Override
      public String getDescription() {
        return ".iface.xml files";
      }
      
      @Override
      public boolean accept(File f) {
        if (f.isDirectory()) return true;
        return f.getAbsolutePath().endsWith(".iface.xml");
      }
    });
    if (JFileChooser.APPROVE_OPTION != fc.showOpenDialog(worker)) return null;
    GrotPanel grot = worker.newGrot();
    FileInputStream inStream = new FileInputStream(fc.getSelectedFile());
    grot.load(inStream);
    inStream.close();
    grot.workingFile = fc.getSelectedFile();
    return grot;
  }
  
  public void update() throws Exception {
    for (AbstractAction1 action : actions) {
      action.update();
    }
  }
  
  private void addAdditionAction(final String name, final Widget widget) {
    additionActions.add(new AbstractAction1() {
      @Override
      public String name() {
        return name;
      }
      
      @Override
      protected void run() throws Exception {
        GrotPanel grot = worker.getActiveGrot();
        if (grot == null) return;
        grot.addWidget(widget.copy().resetUuid());
      }
    });
  }
  
  public final AbstractAction1 copySelection = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      copyToClipboard(grot.selected());
      grot.actContainer.updates.fire();
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 0);
    }
  });
  public final AbstractAction1 cutSelection = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      copyToClipboard(grot.selected());
      grot.deleteSelected();
      grot.actContainer.updates.fire();
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 0);
    }
  });
  
  public final AbstractAction1 paste = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      
      Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
      final WidgetSelection selection;
      try {
        selection = (WidgetSelection)clipboard.getData(WidgetTransfer.FLAVOR);
      } catch (UnsupportedFlavorException err) {
        return;
      }
      Set<UUID> uuids = new HashSet<>();
      ActGroup group = new ActGroup();
      for (Widget widget : selection.data) {
        uuids.add(widget.resetUuid().uuid);
        group.group.add(new AddAct(widget.copy()));
      }
      grot.actContainer.addAct(group);
      grot.selectedUuids.clear();
      grot.selectedUuids.addAll(uuids);
    }
    
    @Override
    protected void update() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      
      setEnabled(false);
      
      Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
      try {
        clipboard.getData(WidgetTransfer.FLAVOR);
        setEnabled(true);
      } catch (UnsupportedFlavorException err) {}
    }
  });
  
  private void copyToClipboard(List<Widget> list) {
    WidgetSelection sel = new WidgetSelection(list);
    WidgetTransfer tra = new WidgetTransfer(sel);
    Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
    clipboard.setContents(tra, new ClipboardOwner() {
      @Override
      public void lostOwnership(Clipboard clipboard, Transferable contents) {}
    });
  }
  
  private void apply(GrotPanel grot, Act act) throws Exception {
    if (act != null) grot.actContainer.addAct(act);
  }
  
  public final AbstractAction1 clearSelection = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      grot.clearSelection();
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 0);
    }
  });
  
  public final AbstractAction1 selectAll = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      grot.selectAll();
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(true);
    }
  });
  
  public final AbstractAction1 alignLeft = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      apply(grot, grot.align.toLeftSideSelected());
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 1);
    }
  });
  
  public final AbstractAction1 alignRight = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      apply(grot, grot.align.toRightSideSelected());
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 1);
    }
  });
  
  public final AbstractAction1 alignTop = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      apply(grot, grot.align.toTopSideSelected());
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 1);
    }
  });
  
  public final AbstractAction1 alignBottom = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      apply(grot, grot.align.toBottomSideSelected());
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 1);
    }
  });
  
  public final AbstractAction1 alignWidths = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      apply(grot, grot.align.toEqualWidthsSelected());
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 1);
    }
  });
  
  public final AbstractAction1 alignHeights = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      apply(grot, grot.align.toEqualHeightsSelected());
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 1);
    }
  });
  
  public final AbstractAction1 alignToVerticalCenter = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      apply(grot, grot.align.toOneVerticalCenterSelected());
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 1);
    }
  });
  
  public final AbstractAction1 alignBetweensVert = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      apply(grot, grot.align.toOneBetweensVertSelected());
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 2);
    }
  });
  public final AbstractAction1 alignBetweensHoriz = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      apply(grot, grot.align.toOneBetweensHorizSelected());
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 2);
    }
  });
  
  public final AbstractAction1 deactivateSelected = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      
      {
        ActGroup act = new ActGroup();
        for (UUID uuid : grot.selectedUuids) {
          act.group.add(new ChangeFieldsAct(uuid, "active", false));
        }
        grot.selectedUuids.clear();
        grot.actContainer.addAct(act);
      }
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      setEnabled(grot.selectedUuids.size() > 0);
    }
  });
  
  public final AbstractAction1 activateAll = add(new AbstractAction1() {
    @Override
    protected void run() throws Exception {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) return;
      
      {
        
        ActGroup act = new ActGroup();
        for (Widget widget : grot.actContainer.storage) {
          if (!widget.active) {
            act.group.add(new ChangeFieldsAct(widget.uuid, "active", true));
          }
        }
        grot.actContainer.addAct(act);
      }
    }
    
    @Override
    protected void update() {
      GrotPanel grot = worker.getActiveGrot();
      if (grot == null) {
        setEnabled(false);
        return;
      }
      for (Widget widget : grot.actContainer.storage) {
        if (!widget.active) {
          setEnabled(true);
          return;
        }
      }
      setEnabled(false);
    }
  });
  
  private final AbstractAction1 incX(final int dx) {
    return new AbstractAction1() {
      @Override
      protected void run() throws Exception {
        GrotPanel grot = worker.getActiveGrot();
        if (grot == null) return;
        List<Widget> selected = grot.selected();
        if (selected.size() == 0) return;
        ActGroup a = new ActGroup();
        for (Widget w : selected) {
          int newX = w.x + dx;
          a.group.add(new ChangeFieldsAct(w.uuid, "x", newX));
        }
        grot.actContainer.mergeAct(a);
      }
    };
  }
  
  public final AbstractAction1 stepRight1 = add(incX(1));
  public final AbstractAction1 stepRight5 = add(incX(5));
  public final AbstractAction1 stepLeft1 = add(incX(-1));
  public final AbstractAction1 stepLeft5 = add(incX(-5));
  
  private final AbstractAction1 incY(final int dy) {
    return new AbstractAction1() {
      @Override
      protected void run() throws Exception {
        GrotPanel grot = worker.getActiveGrot();
        if (grot == null) return;
        List<Widget> selected = grot.selected();
        if (selected.size() == 0) return;
        ActGroup a = new ActGroup();
        for (Widget w : selected) {
          int newY = w.y + dy;
          a.group.add(new ChangeFieldsAct(w.uuid, "y", newY));
        }
        grot.actContainer.mergeAct(a);
      }
    };
  }
  
  public final AbstractAction1 stepDown1 = add(incY(1));
  public final AbstractAction1 stepDown5 = add(incY(5));
  public final AbstractAction1 stepUp1 = add(incY(-1));
  public final AbstractAction1 stepUp5 = add(incY(-5));
  
  private final AbstractAction1 incWidth(final int dw) {
    return new AbstractAction1() {
      @Override
      protected void run() throws Exception {
        GrotPanel grot = worker.getActiveGrot();
        if (grot == null) return;
        List<Widget> selected = grot.selected();
        if (selected.size() == 0) return;
        ActGroup a = new ActGroup();
        for (Widget w : selected) {
          if (w instanceof WidgetRect) {
            WidgetRect r = (WidgetRect)w;
            a.group.add(new ChangeFieldsAct(w.uuid, "width", r.width + dw));
          }
        }
        if (a.group.size() > 0) grot.actContainer.mergeAct(a);
      }
    };
  }
  
  public final AbstractAction1 stepWidthMore1 = add(incWidth(1));
  public final AbstractAction1 stepWidthMore5 = add(incWidth(5));
  public final AbstractAction1 stepWidthLess1 = add(incWidth(-1));
  public final AbstractAction1 stepWidthLess5 = add(incWidth(-5));
  
  private final AbstractAction1 incHeight(final int dh) {
    return new AbstractAction1() {
      @Override
      protected void run() throws Exception {
        GrotPanel grot = worker.getActiveGrot();
        if (grot == null) return;
        List<Widget> selected = grot.selected();
        if (selected.size() == 0) return;
        ActGroup a = new ActGroup();
        for (Widget w : selected) {
          if (w instanceof WidgetRect) {
            WidgetRect r = (WidgetRect)w;
            a.group.add(new ChangeFieldsAct(w.uuid, "height", r.height + dh));
          }
        }
        if (a.group.size() > 0) grot.actContainer.mergeAct(a);
      }
    };
  }
  
  public final AbstractAction1 stepHeightMore1 = add(incHeight(1));
  public final AbstractAction1 stepHeightMore5 = add(incHeight(5));
  public final AbstractAction1 stepHeightLess1 = add(incHeight(-1));
  public final AbstractAction1 stepHeightLess5 = add(incHeight(-5));
}
